/*
 * Config.cpp
 *
 *  Created on: 01.01.2012
 *      Author: mario
 */

#include "Config.h"
#include <boost/property_tree/xml_parser.hpp>
#include <boost/foreach.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <iostream>

static const int CONFIG_VERSION = 1;
static const string DEFAULT_XML =
        "<omnibus config-version='1'>"
        "    <spovnet_name>omnibusX</spovnet_name>"
        "    <listen_on>"
        "        <ip_addr>::</ip_addr>"
        "        <tcp_port>41322</tcp_port>"
        "        <bluetooth_channel>NO</bluetooth_channel>"
        "        <ip>NO</ip>"
        "        <bluetooth_device>NO</bluetooth_device>"
        "    </listen_on>"
        "    <bootstrap>"
        "        <direct>"
        "            <item>"
        "                <host>84.38.67.218</host>"
        "                <port>41325</port>"
        "            </item>"
        "            <item>"
        "                <host>2a00:5080:1:10::1</host>"
        "                <port>41325</port>"
        "            </item>"
        "        </direct>"
        "       <broadcast>YES</broadcast>"
        "       <sdp>NO</sdp>"
        "       <mdns>YES</mdns>"
        "    </bootstrap>"
        "    <flow_control>20</flow_control>"
        "</omnibus>";
        
        
Config::Config()
{
    istringstream istr(DEFAULT_XML);
    boost::property_tree::read_xml(istr, default_pt);
}

Config::~Config()
{
}

void Config::read(const string& path)
{
    this->path = path;
    
    boost::property_tree::read_xml(path, config_pt);

    // check config version
    if ( config_pt.get("omnibus.<xmlattr>.config-version", 0) < CONFIG_VERSION )
        convert_input();
    
}


/* gets a value from ptree, resolve "DEFAULT" entries */
string Config::get(const string& key)
{
    string res;
    
    res = config_pt.get<string>(key, "DEFAULT");
    
    if ( compare_case_insensitive(res, "DEFAULT") )
    {
        res = default_pt.get<string>(key);
    }
    
    return res;
}

/* gets a value as string and cast it to int */
int Config::get_int(const string& key)
{
    int res;
    
    istringstream v(get(key));
    
    v >> res;
    
    return res;
}


string Config::get_ariba_endpoints()
{
    ostringstream ret;
    string tmp_str;

    // tcp port
    tmp_str = get("omnibus.listen_on.tcp_port");
    if ( ! is_no(tmp_str) )
        ret << "tcp{" << tmp_str << "};";

    // bluetooth channel
    tmp_str = get("omnibus.listen_on.bluetooth_channel");
    if ( ! is_no(tmp_str) )
        ret << "rfcomm{" << tmp_str << "};";
    
    // TODO IP und Bluetooth device sind grad noch irgendwie Zukunftsmusik..
    // Vor allem, wie ist es nun wieder mit mehrern IPs....
    
    return ret.str();
}

ptree Config::get_ariba_bootstrap_hints()
{
    ptree ret;
    
    /* bootstrap module "direct" */
    string tmp_str;
    tmp_str = config_pt.get("omnibus.bootstrap.direct.default_items", "NO");
    
    if ( ! is_no(tmp_str) )
    {
        BOOST_FOREACH(ptree::value_type &v, default_pt.get_child("omnibus.bootstrap.direct"))
        {
            ptree endp;
            endp.put("category", "TCPIP");
            endp.put("addr", v.second.get<string>("host"));
            endp.put("port", v.second.get<string>("port"));
            
            ret.add_child("endp", endp);
        };
    }
    
    BOOST_FOREACH(ptree::value_type &v, config_pt.get_child("omnibus.bootstrap.direct"))
    {
        if ( v.first == "item" )
        {
            ptree endp;
            endp.put("category", "TCPIP");
            endp.put("addr", v.second.get<string>("host"));
            endp.put("port", v.second.get<string>("port"));
            
            ret.add_child("endp", endp);
        }
    };
    
    
//    /* others */
//    if ( ! is_no(get("omnibus.bootstrap.broadcast")) )
//        ret << "broadcast;";
//    if ( ! is_no(get("omnibus.bootstrap.sdp")) )
//        ret << "sdp;";
//    if ( ! is_no(get("omnibus.bootstrap.mdns")) )
//        ret << "mdns;";
    
    return ret;
}



// ---------------------------------------------------
// some static convenience functions

bool Config::compare_case_insensitive(const string& str1, const string& str2)
{
    return boost::algorithm::to_lower_copy(str1) == boost::algorithm::to_lower_copy(str2);
}

bool Config::is_yes(const string& str)
{
    return compare_case_insensitive(str, "yes");
}

bool Config::is_no(const string& str)
{
    return compare_case_insensitive(str, "no");
}



// XXX -----------------------------------------------

// temporäre Parserfunktion für config.xml. Die soll hier nicht bleiben.
void parse_endpoint(string& result, string& old, string param)
{
    size_t start, end, len;
    len = param.length();
    start = old.find(param);
    
    if ( start != string::npos )
    {
        end = old.find("}", start+1);
        
        result = old.substr(start+len, end-start-len);
    }
}

void parse_endpoint(vector<string>& result, string& old, string param)
{
    size_t start, end, len;
    len = param.length();

    start = old.find(param);
    while ( start != string::npos )
    {
        end = old.find("}", start+1);
        
        result.push_back(old.substr(start+len, end-start-len));

        start = old.find(param, end);
    }
}


// Convert the old config format into the new one  [historic -> 1]
void Config::convert_input()
{
    ptree new_config_pt;
    
    cout << "Old format of config.xml detected. Converting." << endl;
    
    // read old settings
    string ariba_name = config_pt.get<string>("omnibus.ariba_name");
    string ariba_endpoint = config_pt.get("omnibus.ariba_endpoint", "");
    string ariba_bootstrap_hints = config_pt.get("omnibus.ariba_bootstrap_hints", "");

    /* convert to new ones */
    // endpoints
    string endpoint_tcp = "DEFAULT";
    string endpoint_rfcomm = "NO";
    string endpoint_ip = "DEFAULT";
    string endpoint_bluetooth = "DEFAULT";
    
    parse_endpoint(endpoint_tcp, ariba_endpoint, "tcp{");
    parse_endpoint(endpoint_rfcomm, ariba_endpoint, "rfcomm{");
    parse_endpoint(endpoint_ip, ariba_endpoint, "ip{");
    parse_endpoint(endpoint_bluetooth, ariba_endpoint, "bluetooth{");
    
    if ( endpoint_rfcomm == "10" )
        endpoint_rfcomm = "DEFAULT";
    
    
    // bootstrap
    vector<string> bootstrap_ip;
    vector<string> bootstrap_tcp;
    string bootstrap_broadcast = "NO";
    string bootstrap_mdns = "NO";
    string bootstrap_sdp = "NO";
    parse_endpoint(bootstrap_ip, ariba_bootstrap_hints, "ip{");
    parse_endpoint(bootstrap_tcp, ariba_bootstrap_hints, "tcp{");
    if ( ariba_bootstrap_hints.find("broadcast") != string::npos )
        bootstrap_broadcast = "DEFAULT";
        bootstrap_mdns = "DEFAULT";
    if ( ariba_bootstrap_hints.find("mdns") != string::npos )
        bootstrap_broadcast = "DEFAULT";
        bootstrap_mdns = "DEFAULT";
    if ( ariba_bootstrap_hints.find("sdp") != string::npos )
        bootstrap_sdp = "DEFAULT";
    
    
    /* write new ptree */
    // version
    new_config_pt.put("omnibus.<xmlattr>.config-version", CONFIG_VERSION);
    // username
    new_config_pt.put("omnibus.user_name", ariba_name);
    
    // endpoint
    new_config_pt.put("omnibus.listen_on.tcp_port", endpoint_tcp);
    new_config_pt.put("omnibus.listen_on.bluetooth_channel", endpoint_rfcomm);
    new_config_pt.put("omnibus.listen_on.ip", endpoint_ip);
    new_config_pt.put("omnibus.listen_on.bluetooth_device", endpoint_bluetooth);

    // bootstrap
    ptree& bootstrap_direct_pt = new_config_pt.get_child("omnibus")
            .add_child("bootstrap", ptree())
            .add_child("direct", ptree());
    bootstrap_direct_pt.put("default_items", "NO");
    for (size_t i=0; i < bootstrap_ip.size(); i++)
    {
        string& s = bootstrap_ip[i];
        
        // omnibus server -> default
        if ( s == "83.169.44.84" )
        {
            bootstrap_direct_pt.put("default_items", "YES");
        }
        else
        {            
            ptree item_pt;
            item_pt.put("host", s);
            if ( bootstrap_tcp.size() > i )
                item_pt.put("port", bootstrap_tcp[i]);
            else
                item_pt.put("port", bootstrap_tcp[bootstrap_tcp.size()-1]);
            
            bootstrap_direct_pt.add_child("item", item_pt);
        }
    };
    new_config_pt.put("omnibus.bootstrap.broadcast", bootstrap_broadcast);
    new_config_pt.put("omnibus.bootstrap.sdp", bootstrap_sdp);
    new_config_pt.put("omnibus.bootstrap.mdns", bootstrap_mdns);
    
    // flow control (don't import, always use default)
    new_config_pt.put("omnibus.flow_control", "DEFAULT");
    

    // switch to new config
    config_pt = new_config_pt;
    
    // save updated config
    boost::property_tree::xml_writer_settings<char> w(' ', 4);
    // XXX TEST
    ostringstream out;
    boost::property_tree::write_xml(out, new_config_pt, w);
    cout << "XML OUTPUT" << endl << out.str() << endl;
    
    boost::property_tree::write_xml(this->path, new_config_pt, std::locale(), w);
}
